package com.glsc.ngateway.oaflow.service.xirtrade;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.glsc.ngateway.common.base.domain.mysql.gateway.oaflow.TOaFlowMetaTable;
import com.glsc.ngateway.common.base.domain.mysql.gateway.oaflow.xirtrade.TOaFlowXirTradeConfig;
import com.glsc.ngateway.common.base.domain.mysql.gateway.oaflow.xirtrade.TOaFlowXirTradeInfo;
import com.glsc.ngateway.common.api.common.dto.PageQueryParamDto;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrEmpDto;
import com.glsc.ngateway.common.api.oaflow.dto.other.FlowObject;
import com.glsc.ngateway.common.api.oaflow.dto.xirtrade.TOaFlowXirTradeBaseInfoDto;
import com.glsc.ngateway.common.api.oaflow.dto.xirtrade.TOaFlowXirTradeConfigDto;
import com.glsc.ngateway.common.api.oaflow.dto.xirtrade.TOaFlowXirTradeConfigSearchParamDto;
import com.glsc.ngateway.common.api.oaflow.dto.xirtrade.TOaFlowXirTradeInfoSearchParamDto;
import com.glsc.ngateway.common.base.converter.oaflow.TOaFlowXirTradeConfigConverter;
import com.glsc.ngateway.common.base.converter.oaflow.TOaFlowXirTradeInfoConverter;
import com.glsc.ngateway.common.api.common.dto.PageDto;
import com.glsc.ngateway.common.api.ldap.resp.AppEhrResponse;
import com.glsc.ngateway.common.base.enums.OaFlowXirTradeInfoStatusEnum;
import com.glsc.ngateway.common.base.repo.mysql.gateway.oaflow.TOaFlowXirTradeConfigRepository;
import com.glsc.ngateway.common.base.repo.mysql.gateway.oaflow.TOaFlowXirTradeInfoRepository;
import com.glsc.ngateway.oaflow.exception.OaflowException;
import com.glsc.ngateway.oaflow.service.AddFlowService;
import com.glsc.ngateway.oaflow.service.MailService;
import com.glsc.ngateway.oaflow.service.MetaFlowService;
import com.glsc.ngateway.common.api.ldap.feign.IFeignLdapService;
import com.glsc.ngateway.oaflow.utils.DateUtils;
import com.google.common.base.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import reactor.util.annotation.Nullable;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 同步衡泰Xir系统数据
 */
@Component
public class TOaFlowXirTradeService {

    private static Logger logger = LoggerFactory.getLogger(TOaFlowXirTradeService.class);

    @Resource
    private TOaFlowXirTradeInfoRepository xirTradeInfoRepo;
    @Resource
    private MetaFlowService metaFlowService;
    @Resource
    private AddFlowService addFlowService;
    @Resource
    private IFeignLdapService iFeignLdapService;
    @Resource
    private TOaFlowXirTradeConfigRepository xirTradeConfigRepo;
    @Resource
    private List<AbstractXirTradeFactory> xirTradeFactories;
    @Resource
    private MailService mailService;

    /**
     * TODO:考虑根据不同的衡泰业务，使用模板将各种业务场景区分，或使用工厂按不同场景加工数据
     */
    public List<TOaFlowXirTradeBaseInfoDto> syncXirTrade(@NotBlank String date, @NotNull boolean unPushFlow, @Nullable String type) {
        logger.info("恒泰触发oa原始数据{}", date);
        return xirTradeFactories.stream()
                .filter(factory -> Strings.isNullOrEmpty(type) || factory.getType().equals(type))
                .parallel()
                .map(factory -> {
//                    if (factory.tryLock()) {
//                        logger.info("获取锁成功了{}", factory.getType());
                    try {
                        factory.lock();
                        List<TOaFlowXirTradeBaseInfoDto> allTradeLog = factory.buildUnsyncTradeInfo(date);
                        if (CollectionUtil.isEmpty(allTradeLog)) {
                            logger.info("衡泰指令同步-{}：无指令需同步", factory.getType());
                            return new ArrayList<TOaFlowXirTradeBaseInfoDto>(0);
                        } else {
                            logger.info("衡泰指令同步-{}，合成结果{}", factory.getType(), allTradeLog);
                            allTradeLog.forEach(log -> pushOaFlow(log, unPushFlow));
                            return allTradeLog;
                        }
                    } catch (Exception e) {
                        logger.error("衡泰xir同步异常{}", factory.getType(), e);
                        return new ArrayList<TOaFlowXirTradeBaseInfoDto>(0);
                    } finally {
                        factory.unlock();
                    }
//                    } else {
//                        logger.info("获取锁失败了{}", factory.getType());
//                        return new ArrayList<TOaFlowXirTradeBaseInfoDto>(0);
//                    }
                })
                .filter(CollectionUtil::isNotEmpty)
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
    }

    /**
     * @param tradeInfo 衡泰指令创建OA流程的信息
     * @Description 根据衡泰指令信息，创建对应的OA流程
     */
    private <T extends TOaFlowXirTradeBaseInfoDto> void pushOaFlow(@NotNull T tradeInfo, @NotNull boolean unPushFlow) {
        try {
            String workflowId = tradeInfo.getWorkflowId();
            //创建oa流程
            logger.info("开始创建流程，FlowId:" + workflowId + ",参数:" + tradeInfo + ",时间:" + DateUtils.getCurrentDate(DateUtils.formatSecond));
            //转义字段列表
            Map<String, Object> dataMap = JSONUtil.parseObj(tradeInfo);
            //OA流程标题
            dataMap.put("title", tradeInfo.getTitle());
            tradeInfo.setRequestTitle(tradeInfo.getTitle());
            //创建人
            dataMap.put("creater", tradeInfo.getApplicant());
            AppEhrResponse<LdapEhrEmpDto> resp = iFeignLdapService.getLdapEhrEmp(tradeInfo.getApplicant(), null, false);
            if (Objects.isNull(resp) || !resp.isSuccess() || Objects.isNull(resp.getData())) {
                logger.info("创建流程失败，未找到oa流程发起人，{}", tradeInfo.getApplicant());
                return;
            }
            LdapEhrEmpDto appInfo = resp.getData();
            //公司
            dataMap.put("applicantComp", appInfo.getOaCompId().toString());
            //部门
            dataMap.put("applicantDepart", appInfo.getOaDeptId().toString());
            //申请日期
            dataMap.put("applyDate", DateUtils.getCurrentDate(DateUtils.formatDate_));
            if (unPushFlow) {
                logger.info("不需要创建流程：" + tradeInfo);
                tradeInfo.setDealStatus(OaFlowXirTradeInfoStatusEnum.SYNCED.getCode());
            } else {
                List<TOaFlowMetaTable> metaList = metaFlowService.getFlowMetaDataList(workflowId);
                //组装创建流程请求报文
                FlowObject fo = addFlowService.operateOAMessage(workflowId, dataMap, metaList);
                fo.setIsnextflow(tradeInfo.getIsnextflow());
                logger.info("创建流程调用参数为：" + JSONObject.toJSON(fo).toString());
                Map<String, String> resultMap = addFlowService.addOaFlow(JSONObject.toJSON(fo).toString());
                Integer requestId = Integer.valueOf(resultMap.get("requestid"));
                if (requestId > 0) {
                    logger.info("完成流程创建，FlowId:" + workflowId + ",流程requestid:" + requestId + ",时间:" + DateUtils.getCurrentDate(DateUtils.formatSecond));
                    tradeInfo.setRequestId(String.valueOf(requestId));
                    tradeInfo.setDealStatus(OaFlowXirTradeInfoStatusEnum.SYNCED.getCode());
                } else {
                    logger.error("流程创建失败，FlowId:" + workflowId + ",流程requestid:" + requestId + ",时间:" + DateUtils.getCurrentDate(DateUtils.formatSecond));
                    tradeInfo.setDealStatus(OaFlowXirTradeInfoStatusEnum.FAILED.getCode());
                    //TODO 短信通知系统管理员
                    mailService.sendMailToAdmin("xir流程创建失败,wfid"+workflowId,JSONObject.toJSON(fo).toString());
                }
            }
            xirTradeInfoRepo.save(TOaFlowXirTradeInfoConverter.dto2Do(tradeInfo));
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            logger.error("衡泰指令同步创建oa流程异常", e.getMessage());
        } catch (Exception e) {
            logger.error("衡泰指令同步创建oa流程异常", e.getMessage());
        }
    }

    /**
     * 查看记录
     */
    public PageDto<TOaFlowXirTradeBaseInfoDto> query(PageQueryParamDto<TOaFlowXirTradeInfoSearchParamDto> param) {
        param.setParam(Objects.isNull(param.getParam()) ? new TOaFlowXirTradeInfoSearchParamDto() : param.getParam());
        Pageable pageable = param.getPageable();
        Page<TOaFlowXirTradeInfo> pageRes = xirTradeInfoRepo.page(pageable, param.getParam());
        List<TOaFlowXirTradeBaseInfoDto> dto = TOaFlowXirTradeInfoConverter.do2Dto(pageRes.getContent());
        return new PageDto<>(dto, pageable.getPageNumber(), pageable.getPageSize(), pageRes.getTotalElements());
    }

    /**
     * 删除
     */
    @Transactional(rollbackFor = Exception.class)
    public void delete(@NotNull Long id) {
        xirTradeInfoRepo.deleteById(id);
    }

    /**
     * 分页查询配置项
     */
    public PageDto<TOaFlowXirTradeConfigDto> queryConfig(PageQueryParamDto<TOaFlowXirTradeConfigSearchParamDto> param) {
        param.setParam(Objects.isNull(param.getParam()) ? new TOaFlowXirTradeConfigSearchParamDto() : param.getParam());
        Pageable pageable = param.getPageable();
        Page<TOaFlowXirTradeConfig> pageRes = xirTradeConfigRepo.page(pageable, param.getParam());
        List<TOaFlowXirTradeConfigDto> domain = TOaFlowXirTradeConfigConverter.do2Dto(pageRes.getContent());
        return new PageDto<>(domain, pageable.getPageNumber(), pageable.getPageSize(), pageRes.getTotalElements());
    }

    public List<TOaFlowXirTradeConfigDto> queryAllConfig(@NonNull TOaFlowXirTradeConfigSearchParamDto param) {
        return xirTradeConfigRepo.queryAll(param).stream().map(TOaFlowXirTradeConfigConverter::do2Dto).collect(Collectors.toList());
    }

    /**
     * 删除配置
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteConfig(@NotNull Long id) {
        xirTradeConfigRepo.deleteById(id);
    }

    /**
     * 添加/修改配置
     */
    @Transactional(rollbackFor = Exception.class)
    public void addOrUpdateConfig(@NotNull TOaFlowXirTradeConfigDto config) {
        //检查是否有冲突事件的配置
        List<TOaFlowXirTradeConfig> exist = xirTradeConfigRepo.findAllByWorkflowIdIn(List.of(config.getWorkflowId()));
        if (CollectionUtil.isNotEmpty(exist)) {
            throw new OaflowException("已存在相同配置");
        }
        xirTradeConfigRepo.save(Objects.requireNonNull(TOaFlowXirTradeConfigConverter.dto2Do(config)));
    }
}
